import {WELLS_COLUMN, WELLS_ROW} from "../pages/main/plate/config";
import {BASELINE_CORRECTION, DATA_ANALYSIS_METHOD} from "../pages/main/analysis/components/quantification/config";
import React from "react";
import {AMPLIFICATION, MELT} from "../pages/main/protocol/config";
import {WELL_TYPE} from "../pages/main/store/constants";
import * as XLSX from 'xlsx';
/**
 *
 * 获取上下文路径
 *
 * @returns {string}
 */
export function getContextPath() {
  let strPath = window.location.pathname;
  return strPath.substring(0, strPath.substr(1).indexOf('/') + 1);
}

/**
 *
 * 深克隆
 *
 * @param origin {Object}  原对象(被克隆对象)
 * @param target {...Object}  目标对象(克隆对象)
 * @returns {Object}   target
 */
export function deepClone(origin, target = {}) {
  for (let prop in origin) {
    if (origin.hasOwnProperty(prop)) {  //  for..in会遍历出原型中的属性
      if (origin[prop] !== null && typeof (origin[prop]) === "object") {
        Array.isArray(origin[prop]) ? target[prop] = [] : target[prop] = {};
        deepClone(origin[prop], target[prop]);
      } else {
        target[prop] = origin[prop];
      }
    }
  }
  return target;
}

/**
 *
 * 返回当前具体时间
 *
 * @returns {string}  返回当前时间 年月日-时分秒
 */
export function getCurrentTime() {
  let date = new Date(),
    year = date.getFullYear(),
    month = (date.getMonth() + 1 + "").padStart(2, "0"),
    day = (date.getDate() + "").padStart(2, "0"),
    hours = (date.getHours() + "").padStart(2, "0"),
    minutes = (date.getMinutes() + "").padStart(2, "0"),
    seconds = (date.getSeconds() + "").padStart(2, "0");
  return year + month + day + "-" + hours + minutes + seconds;
}

/**
 *
 * 获取ScanChannel对像
 *
 * @returns {Array} 返回ScanChannel对象
 */
export function getScanChannel() {
  let scanChannel = [];
  if (!JSON.parse(localStorage.getItem("channel0"))) {
    return [
      {
        lists: [{name: "FAM", isSelected: true}, {name: "SYBR", isSelected: true}/*, {name: "EvaGreen", isSelected: true}*/],
        showName: "FAM",
        scan: true,
        forbidden: false
      },
      {
        lists: [{name: "VIC", isSelected: true}, {name: "HEX", isSelected: true}, {name: "JOE", isSelected: true}/*, {name: "540", isSelected: true}, {name: "560", isSelected: true}*/],
        showName: "VIC",
        scan: true,
        forbidden: false
      },
      {
        lists: [{name: "TAMRA", isSelected: true}, {name: "CY3", isSelected: true}],
        showName: "CY3",
        scan: true,
        forbidden: false
      },
      {
        lists: [{name: "ROX", isSelected: true}, {name: "TEX615", isSelected: true}, {name: "TEXAS RED", isSelected: true}/*, {name: "610", isSelected: true}*/],
        showName: "ROX",
        scan: true,
        forbidden: false
      },
      {
        lists: [{name: "CY5", isSelected: true}/*, {name: "LIZ", isSelected: true}, {name: "Mustang Purple", isSelected: true}, {name: "Quasar 670", isSelected: true}*/],
        showName: "CY5",
        scan: true,
        forbidden: false
      },
      {
        lists: [{name: "CY5.5", isSelected: true}, {name: "Quasar 705", isSelected: true}],
        showName: "Quasar 705",
        scan: true,
        forbidden: false
      }
    ];
  }
  for (let i = 0; i < 6; i++) {
    scanChannel.push(JSON.parse(localStorage.getItem(`channel${i}`)));
  }
  return [
    {lists: scanChannel[0], showName: "FAM", scan: true, forbidden: false},
    {lists: scanChannel[1], showName: "VIC", scan: true, forbidden: false},
    {lists: scanChannel[2], showName: "CY3", scan: true, forbidden: false},
    {lists: scanChannel[3], showName: "ROX", scan: true, forbidden: false},
    {lists: scanChannel[4], showName: "CY5", scan: true, forbidden: false},
    {lists: scanChannel[5], showName: "Quasar 705", scan: true, forbidden: false}
  ];
}

//对数组中的对象按照某个属性进行排序
export function sortByProperty(arr, property) {
  arr.sort(function (a, b) {
    if (a[property] < b[property]) {
      return -1;
    }
    if (a[property] > b[property]) {
      return 1;
    }
    return 0;
  });
  return arr;
}

/*
 * 获取protocol里channel通道选择情况及扫描通道更新
 * */
export function getProtocolChannel() {
  let scanChannel = getScanChannel();
  scanChannel.forEach(it => {
    it.lists.forEach((item, index) => {
      //把channel里List为空的的删除干净
      !item.name && it.lists.splice(index, 1);
      //删除起始位置，删除长度
    });
    if (it.lists.every(item => !item.isSelected)) {
      it.scan = false;
      it.forbidden = true;
    }
  });
  return {channel: scanChannel.map(item => item.scan ? 1 : 0), scanChannels: scanChannel};
}


/***
 *
 * 获取16进制随机颜色
 *
 * @returns {string} "#XXXXXX" 16进制颜色
 */
export function getRandomColor() {
  return "#" + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, "0");
}

/***
 *
 * 获取当前年
 *
 * @returns {number}
 */
export function getCurrentYear() {
  return new Date().getFullYear();
}

/***
 *
 * 获取当前月
 *
 * @returns {string}
 */
export function getCurrentMonth() {
  const monthName = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEV"];
  return monthName[new Date().getMonth()];
}

/***
 *
 * 获取当前月
 *
 * @returns {number}
 */
export function getCurrentMonthNumber() {
  return new Date().getMonth() + 1;
}

/***
 *
 * 获取当前日
 *
 * @returns {number}
 */
export function getCurrentDay() {
  return (new Date().getDate() + "").padStart(2, "0");
}

/***
 *
 * 获取当前时
 *
 * @returns {number}
 */
export function getCurrentHours() {
  return (new Date().getHours() + "").padStart(2, "0");
}

/***
 *
 * 获取当前分
 *
 * @returns {number}
 */
export function getCurrentMinutes() {
  return (new Date().getMinutes() + "").padStart(2, "0");
}

/***
 *
 * 获取当前秒
 *
 * @returns {number}
 */
export function getCurrentSeconds() {
  return (new Date().getSeconds() + "").padStart(2, "0");
}

/***
 *
 * 将数字转换为科学计数法(只适用大于1的数)
 *
 * @param value 原始数字
 * @returns {string}  科学计数法
 */
export function count2ScientificCount(value) {
  let reg = /^([1-9]\d*(\.\d+)?)|^(0.\d+)([eE][+\-]?\d+)?$/;
  let result = value.match(reg), num = Number(value);
  if (result === null || num === 0) {
    return "1.000E+0";
  } else {
    let last = Math.floor(Math.log10(num)),
      first = (num / (10 ** last)).toFixed(3);
    return `${first}E${last < 0 ? last : "+" + last}`;
  }
}

/***
 *
 * 返回时间
 *
 * @param time  秒
 * @returns {string}  HH:MM:SS
 */
export function calcTime(time) {
  let hours = (Math.floor(time / 3600) + "").padStart(2, "0"),
    minutes = (Math.floor(time % 3600 / 60) + "").padStart(2, "0"),
    second = (time % 60 + "").padStart(2, "0");
  return `${hours}:${minutes}:${second}`;
}

/***
 *
 * 返回时间
 *
 * @param time  秒
 * @returns {string}  MM:SS
 */
export function calcMMSS(time) {
  let minutes = (Math.floor(time % 3600 / 60) + ""),
    second = (time % 60 + "").padStart(2, "0");
  return `${minutes}mm:${second}ss`;
}

/***
 *
 * 对加载的文件数据进行处理
 *
 * @param loadData  加载的文件数据
 * @returns {{targetSelectedArr: *[], loadData: *, analysisShowLineArr: *[]}}
 */
export function modifyData(loadData) {
  calCqMean(loadData);  //  重新计算 cqMean
  calQuantityMean(loadData);  //  重新计算 sqMean
  // 重新计算sampleColor
  loadData.wells.forEach(well => loadData.plateSetting.samples.forEach(item => well.sampleName && well.sampleUuid === item.uuid && (well.sampleColor = item.color)));
  if (!loadData.quanlityControl) {
    loadData.quanlityControl = {
      negCq: 38,
      ntcCq: 38,
      posCq: 35,
      efficiency: {
        min: 90,
        max: 110
      },
      stdCurveR2: 0.98,
      cqStdDev: 0.5
    };
  }
  loadData.targets.forEach(it => {
    const index = loadData.plateSetting.targets.findIndex(t => t.name === it.name);
    if (index === -1) {
      return;
    }
    loadData.plateSetting.targets[index] = it;
  });
  for (let i = 0; i < loadData.meltDataOrigin.length; i++) {
    for (let j = 1; j < loadData.meltDataOrigin[i].length - 1; j++) {
      if (loadData.meltDataOrigin[i][j] === 0) {
        loadData.meltDataOrigin[i][j] = (loadData.meltDataOrigin[i][j - 1] + loadData.meltDataOrigin[i][j + 1]) / 2;
      }
    }
  }
  loadData.wells.forEach((well, index) => {
    well.targets.forEach((it, index) => {
      const target = loadData.targets.find(target => target.name === it.name);
      if (index === undefined) {
        return;
      }
      well.targets[index] = target;
    });
    for (let i = 0; i < well.lines.length; i++) {
      if (!well.lines[i].sigLevel) {
        well.lines[i].sigLevel = 5.0;
      }
    }
    // console.log('打印',index,well.meltData);
    //TODO:well的meltData必须为一维数组，且数据对应，检查转换为正确数据
    if (Array.isArray(well.meltData[0])) {
      const azdIndex = well.col * 8 + well.row; //  AZD中的数据是竖着算的,D3对应19,cqa中D3对应38
      console.warn('硬件端仪器保存cqa-meltData数据异常');
      const meltData = well.meltData;
      const meltDataOrigin = loadData.meltDataOrigin.length === 0 ? meltData : loadData.meltDataOrigin;
      well.meltData = meltDataOrigin[azdIndex];
    }
    //0的情况兜底
    for (let i = 1; i < well.meltData.length - 1; i++) {
      if (well.meltData[i] === 0) {
        well.meltData[i] = (well.meltData[i - 1] + well.meltData[i + 1]) / 2;
      }
    }
  });
  const lineData = getAnalysisInfo(loadData),
    analysisShowLineArr = lineData.analysisShowLineArr,
    targetSelectedArr = lineData.targetSelectedArr;
  // 加载cqa文件后更新endogenousControl
  let endogenousControlTarget = [];
  loadData.plateSetting.targets.forEach(item => item.name && endogenousControlTarget.push({
    opt: false,
    name: item.name,
    uuid: item.uuid
  }));
  // 加载cqa文件后更新geneExpression界面的experimentSetting
  let experimentSetting = {
    targets: [],
    samples: [],
    groups: []
  };
  let groupWellList = loadData.wells.filter(well => well.groupUuid !== '' && well.groupColor !== '');
  let arr = [], obj = {};
  groupWellList.forEach(item => {
    if (obj.hasOwnProperty(item.groupName)) {

    } else {
      obj[item.groupName] = item.groupName;
      arr.push({
        name: item.groupName,
        reference: false,
        color: item.groupColor,
        showChart: true,
        type: item.type
      });
    }
  });

  experimentSetting.groups = arr;
  loadData.plateSetting.targets.forEach(item => {
    experimentSetting.targets.push({
      name: item.name,
      control: false,
      color: item.color,
      e: item.e > 0 ? item.e : 1,
      showChart: true
    });
  });
  loadData.plateSetting.samples.forEach(item => {
    experimentSetting.samples.push({
      name: item.name,
      reference: false,
      color: item.color,
      showChart: true,
      type: item.type
    });
  });
  loadData.controlTray = 0;
  loadData.analysisMethodSetting.method = BASELINE_CORRECTION;
  return {
    loadData,
    targetSelectedArr,
    analysisShowLineArr,
    endogenousControlTarget,
    experimentSetting
  };
}

/***
 *
 * 获取分析界面数据
 *
 * @param data 大对象
 * @returns {{targetSelectedArr: [], analysisShowLineArr: []}}  targetSelectedArr:包含analysis界面中Target Setting被选中的target analysisShowLineArr:包含可以画线的所有数据
 */
export function getAnalysisInfo(data) {
  let dataTargets = data.targets;
  let wells = data.wells;
  let targetSelectedArr = [], analysisShowLineArr = [];
  let hasMelt = false;  //  判断是否有melt，有则进行找侧峰的操作
  if (data.protocol.stages.some(item => item.state === MELT)) {
    hasMelt = true;
  }
  wells.forEach(well => {
    if (!well.sampleName) {
      return;
    }
    let tm2SampleSearch = [WELL_TYPE.UNK, WELL_TYPE.POS, WELL_TYPE.STD];
    let tm2 = "-", peakHigh2 = "-";
    let allPeakArr = [];
    if (hasMelt && tm2SampleSearch.find(it => it === well.sampleName)) {  //  找侧锋
      if (well.meltPeak.length > 3) {
        for (let i = 1; i < well.meltPeak.length - 1; i++) {
          if (well.meltPeak[i] > well.meltPeak[i - 1] && well.meltPeak[i] > well.meltPeak[i + 1]) { //  比前后两个数大
            allPeakArr.push({value: well.meltPeak[i], pos: i});
          }
        }
        allPeakArr.sort((a, b) => b.value - a.value);
        if (allPeakArr.length > 1) {
          if (allPeakArr[1].value > allPeakArr[0].value / 3) {
            peakHigh2 = allPeakArr[1].value;
            tm2 = allPeakArr[1].pos;
          }
        }
      }
    }
    well.targets.forEach(target => {  //  同时拥有sample和target才可以画线
      let dataTarget = dataTargets.find(it => it.name === target.name); //  每个well中的target和外面的总target不一致,都用外面的
      let channelIndex = target.channelIndex; //  base 0
      if (!target) {
        return;
      }
      targetSelectedArr.push(target.name);
      let cq = typeof well.lines[channelIndex].cq === "number" ? well.lines[channelIndex].cq.toFixed(2) : -1,
        cqMean = typeof well.lines[channelIndex].cqMean === "number" ? well.lines[channelIndex].cqMean.toFixed(2) : -1,
        cqSd = typeof well.lines[channelIndex].cqSd === "number" ? well.lines[channelIndex].cqSd.toFixed(3) : -1,
        quantity = typeof well.lines[channelIndex].sq === "number" ? well.lines[channelIndex].sq.toExponential(6) : -1,
        quantityMean = well.lines[channelIndex].sqMean,
        quantitySd = well.lines[channelIndex].sqSd,
        // quantityEfficiency = (dataTarget.e * 100).toFixed(2),
        quantityEfficiency = dataTarget ? (dataTarget.e * 100).toFixed(2) : NaN,
        // r = Number(dataTarget.r2).toFixed(3);
        r = dataTarget ? Number(dataTarget.r2).toFixed(3) : NaN;
      analysisShowLineArr.push({
        col: well.col,
        row: well.row,
        id: well.row * 12 + well.col,
        channelIndex: channelIndex,
        color: target.color,
        sampleColor: well.sampleColor,
        channel: target.channel,
        well: {show: true, value: WELLS_ROW[well.row] + WELLS_COLUMN[well.col]},
        omit: {show: true, value: well.lines[channelIndex].omit + ""},  //  后面会用这个omit是否存在来判断,这里false要用字符串,否则会在判断有没有omit属性时发生冲突
        sigLevel: {show: true, value: well.lines[channelIndex].sigLevel},
        flag: {show: true, value: "NA"},
        sample: {show: true, value: well.sampleName},
        target: {show: true, value: target.name},
        task: {show: true, value: well.type || "NA"},
        dye: {show: true, value: target.channel},
        cq: {show: true, value: parseInt(cq) === -1 ? "NA" : cq, omitValue: "-"},
        cqMean: {show: true, value: parseInt(cqMean) === -1 ? "NA" : cqMean, omitValue: "-"},
        cqSd: {show: true, value: parseInt(cqSd) === -1 ? "NA" : cqSd, omitValue: "-"},
        quantity: {show: true, value: parseInt(quantity) === -1 ? "NA" : quantity, omitValue: "-"},
        quantityMean: {
          show: true,
          value: parseInt(quantityMean) === -1 ? "NA" : quantityMean.toExponential(2),
          omitValue: "-"
        },
        quantitySd: {
          show: true,
          value: parseInt(quantitySd) === -1 ? "NA" : quantitySd.toExponential(2),
          omitValue: "-"
        },
        quantityEfficiency: {
          show: true,
          value: isNaN(quantityEfficiency) || quantityEfficiency < 0 ? "NA" : quantityEfficiency + "%",
          omitValue: "-"
        },
        r: {show: true, value: parseInt(r) === -1 ? "NA" : r, omitValue: "-"},
        comments: {show: true, value: target.comments || "Null"},
        baseStartCycle: {show: true, value: well.lines[channelIndex].baseStartCycle},
        baseEndCycle: {show: true, value: well.lines[channelIndex].baseEndCycle},
        tm1: well.meltTemperature < 0 ? "-" : well.meltTemperature,
        tm2: tm2,
        peakHigh1: Math.max(...well.meltPeak) < 0 ? "-" : Math.max(...well.meltPeak),
        peakHigh2: peakHigh2
      });
      // console.log(analysisShowLineArr);
    });
  });
  return {
    targetSelectedArr,
    analysisShowLineArr
  };
}

/***
 *
 * @returns {string}
 */
export function generateUUID() {
  let d = Date.now();
  if (window.performance && typeof window.performance.now === "function") {
    d += performance.now(); // use high-precision timer if available
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    let r = (d + Math.random() * 16) % 16 | 0;
    d = Math.floor(d / 16);
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  });
}

/**
 * data.targets与data.plateSetting返回一致处理
 * @param data
 */
export const updateTargetsWithPlateTargets = data => {
  data.targets.forEach(it => {
    const index = data.plateSetting.targets.findIndex(t => t.name === it.name);
    if (index === -1) {
      return;
    }
    data.plateSetting.targets[index] = it;
  });
};

/**
 *
 * 获取保存Excel的数据
 *
 * @param indey
 * @param analysisData
 * @param technicalTableData
 * @param allelicTableData
 * @param failureReport
 * @param endPointSetting
 * @param originData
 * @param method
 * @returns {{head: [], name: string, body: []}}
 */
export function getExcelData(indey, analysisData, technicalTableData, allelicTableData, failureReport, endPointSetting, originData, method) {
  let excel = {
    name: "",
    head: [],
    body: []
  };
  switch (indey) {
    case 0: //  Quantification Results
      excel.name = "Quantification Results";
      excel.head = [["Well", "Sample Name", "Target Name", "Task", "Dyes", "Cq", "Cq Mean", "Cq SD", "Quantity", "Quantity Mean", "Quantity SD", "Baseline start", "Baseline end", "Comments"]];
      analysisData.forEach(item => excel.body.push([
        item.well.value, item.sample.value, item.target.value, item.task.value, item.dye.value, item.cq.value, item.cqMean.value, item.cqSd.value, item.quantity.value, item.quantityMean.value + "",
        item.quantitySd.value + "", item.baseStartCycle.value + "", item.baseEndCycle.value + "", item.comments.value
      ]));
      break;
    case 1: //  Melt Data
      excel.name = "Melt Data";
      excel.head = [["Well", "Sample", "Target", "Task", "Dyes", "Tm1", "Tm2", "Tm3", "Peak High1", "Peak High2", "Peak High3", "Comments"]];
      analysisData.forEach(item => excel.body.push([
        item.well.value, item.sample.value, item.target.value, item.task.value, item.dye.value, item.tm1 + "", item.tm2 + "", item.tm3 + "", item.peakHigh1 + "", item.peakHigh2 + "",
        item.peakHigh3 + "", item.comments.value
      ]));
      break;
    case 2: //  RFU spreadsheet
      let cycleNum = 0, title = [], index = 1;
      originData.protocol.stages.forEach(item => item.state === AMPLIFICATION && (cycleNum += item.cycle));
      title = Array(cycleNum).fill(null).map((it, index) => index + 1);
      excel.name = "RFU spreadsheet";
      excel.head = [["Well/Cycle", "Target", ...title]];
      analysisData.forEach(item => excel.body.push([
        item.well.value, item.target.value, ...originData.wells[item.id].lines[item.channelIndex][DATA_ANALYSIS_METHOD[method]].map(it => (it * 1000).toFixed(3))
      ]));
      break;
    case 3: //  RFU melt
      let start = 0, end = 0, gradient = 0, result = [];
      originData.protocol.stages.forEach(item => {
        if (item.state === MELT) {
          start = item.steps[0].temperature;
          end = item.steps[1].temperature;
          gradient = item.steps[0].gradient;
          while (start <= end) {
            result[result.length] = start;
            start += gradient;
          }
        }
      });
      excel.name = "RFU melt";
      excel.head = [["Well/Temp", ...result]];
      analysisData.forEach(item => {
        excel.body.push([
          item.well.value, ...originData.wells[item.id].meltData.map(it => (it * 1000).toFixed(3))
        ]);
      });
      break;
    case 4: //  Gene Expression data
      excel.name = "Gene Expression data";
      excel.head = [["#", "Omit", "Sample", "Target", "Cq Mean", "ΔCq Mean", "ΔCq SD", "ΔΔCq", "RQ", "RQ Min", "RQ Max"]];
      technicalTableData.forEach(item => excel.body.push([
        item.tag.value + "", item.omit.value + "", item.biological.value, item.target.value, Number(item.cqMean.value.toFixed(3)) + "", Number(item._cqMean.value.toFixed(3)) + "",
        Number(item._cqSd.value.toFixed(3)) + "",
        Number(item.__cq.value.toFixed(3)) + "", Number(item.rq.value.toFixed(3)) + "", Number(item.rqMin.value.toFixed(3)) + "", Number(item.rqMax.value.toFixed(3)) + ""
      ]));
      break;
    case 5: //  Allelic Discrimination
      excel.name = "Allelic Discrimination";
      excel.head = [["Well", "Omit", "Sample Name", "Task", "Allele1 Cq", "Allele2 Cq"]];
      allelicTableData.forEach(item => excel.body.push([
        item.well, item.omit + "", item.sampleName, item.task, item.allele1Cq, item.allele2Cq
      ]));
      break;
    case 6: //  QC Summary
      excel.name = "QC Summary";
      excel.head = [["Well", "NEG", "NTC", "POS", "STD", "EFF", "R2", "RDEV"]];
      failureReport.forEach(item => excel.body.push([
        item.well, item.neg + "", item.ntc + "", item.pos + "", item.std + "", item.eff + "", item.r2 + "", item.rdev + ""
      ]));
      break;
    case 7: //  Determination Data
      excel.name = "Determination Data";
      excel.head = [["Target", "Channel", "Ave Cycles", "RFU", "RFU Lowest", "RFU Highest", "NTC Ave", "CutOff"]];
      endPointSetting.forEach(item => excel.body.push([
        item.target, item.channel, item.cycleStatus, item.rfu, Number(item.rfuLowest.toFixed(3)), Number(item.rfuHighest.toFixed(3)), item.ntcAve, item.cutOff
      ]));
      break;
  }
  return excel;
}

/**
 * 导出excel
 * @param {*} headers
 * @param {*} excelList
 * @param {*} fileName
 */
export const exportExcel = (headers, excelList, fileName = 'demo.xlsx') => {
  const _headers = headers
    .map((item, i) => Object.assign({}, {key: item.key, title: item.title, position: String.fromCharCode(65 + i) + 1}))
    .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {key: next.key, v: next.title}}), {});
  const _data = excelList
    .map((item, i) => headers.map((key, j) => Object.assign({}, {content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2)})))
    // 对刚才的结果进行降维处理（二维数组变成一维数组）
    .reduce((prev, next) => prev.concat(next))
    // 转换成 worksheet 需要的结构
    .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.content}}), {});

  // 合并 headers 和 data
  const output = Object.assign({}, _headers, _data);
  // 获取所有单元格的位置
  const outputPos = Object.keys(output);
  // 计算出范围 ,["A1",..., "H2"]
  const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`;

  // 构建 workbook 对象
  const wb = {
    SheetNames: ['pcrAutoSample'],
    Sheets: {
      pcrAutoSample: Object.assign(
        {},
        output,
        {
          '!ref': ref,
          '!cols': [{wpx: 80}, {wpx: 80}, {wpx: 80}, {wpx: 60}, {wpx: 60}, {wpx: 60}, {wpx: 180}, {wpx: 180}]
        }
      )
    }
  };

  // 导出 Excel
  XLSX.writeFile(wb, fileName);
};


/***
 *
 * 排序功能
 *
 * @param arr
 * @param indey
 * @returns {*}
 */
export function groupBySort(arr, indey) {
  switch (indey) {
    case 0:
      arr.sort((a, b) => a.target.value.localeCompare(b.target.value));
      break;
    case 1:
      arr.sort((a, b) => a.sample.value.localeCompare(b.sample.value));
      break;
    case 2:
      arr.sort((a, b) => a.task.value.localeCompare(b.task.value));
      break;
    case 3:
      arr.sort((a, b) => a.channelIndex - b.channelIndex);
      break;
    case 4:
      arr.sort((a, b) => a.cq.value.localeCompare(b.cq.value));
      break;
    case 5: //  TODO:  RQ
      arr.sort((a, b) => a.cq.value - b.cq.value);
      break;
    case 6: //  TODO:  Flag
      arr.sort((a, b) => a.cq.value - b.cq.value);
      break;
    case 7:
      arr.sort((a, b) => a.row - b.row);
      break;
    case 8:
      arr.sort((a, b) => a.col - b.col);
      break;
  }
  return arr;
}

/***
 *
 * 节流函数
 *
 * @param fn
 * @param delay
 * @returns {function(...[*]=)}
 */
export function throttling(fn, delay) {
  let time = 0;
  return function () {
    let now = Date.now();
    if (now - time > delay) {
      time = now;
      fn.apply(this, arguments);
    }
  };
}

/**
 *
 * 数组中的值乘1000
 *
 * @param arr
 */
export function multiply1000(arr) {
  return arr.map(it => it * 1000);
}

/***
 *
 * 数组排序去重
 *
 * @param arr
 * @returns {[*]}
 */
export function sortAndUnique(arr) {
  let unique = [];
  for (let i = 0; i < arr.length; i++) {
    if (unique.includes(arr[i])) {
      continue;
    }
    unique.push(arr[i]);
  }
  unique.sort((a, b) => a - b);
  return unique;
}

/***
 *
 * 计算cqMean
 *
 * @param data
 * @returns data
 */
export function calCqMean(data) {
  data.targets.forEach(target => {
    let targetName = target.name, channelIndex = target.channelIndex, wellList = [];
    //  取出 target 一致的 well
    data.wells.forEach(item => item.targets.some(it => it.name === targetName) && wellList.push(item));
    let set = new Set();
    wellList.forEach(well => {
      let sampleName = well.sampleName;
      if (set.has(sampleName)) {
        return;
      }
      set.add(sampleName);
      let sum = 0, num = 0, s = 0;
      //  将所有 sampleName 一致的 cq 作累加
      wellList.forEach(item => {
        if (sampleName !== item.sampleName) {
          return;
        }
        let cq = item.lines[channelIndex].cq;
        if (cq === -1) {
          return;
        }
        sum += cq;
        num++;
      });
      if (num === 0) {
        return;
      }
      let cqMean = sum / num;
      // 对所有 sample name 一致的 cqMean 赋值。同时为计算 cqSd 做准备
      wellList.forEach(item => {
        let index = item.row * 12 + item.col;
        if (sampleName !== item.sampleName) {
          return;
        }
        let cq = item.lines[channelIndex].cq;
        if (cq === -1) {
          return;
        }
        data.wells[index].lines[channelIndex].cqMean = cqMean;
        s += Math.pow(cq - cqMean, 2);
      });
      // 对所有 sampleName 一致的 cqSd 赋值。
      let cqSd = Math.sqrt(s / num);
      wellList.forEach(item => {
        let index = item.row * 12 + item.col;
        if (sampleName !== item.sampleName) {
          return;
        }
        let cq = item.lines[channelIndex].cq;
        if (cq === -1) {
          return;
        }
        data.wells[index].lines[channelIndex].cqSd = cqSd;
      });
    });
  });
}

/***
 *
 * 计算quantity mean
 *
 * @param data
 * @returns data
 */
export function calQuantityMean(data) {
  data.targets.forEach(target => {
    let targetName = target.name, channelIndex = target.channelIndex, wellList = [];
    //  取出 target 一致的 well
    data.wells.forEach(item => item.targets.some(it => it.name === targetName) && wellList.push(item));
    let set = new Set();
    wellList.forEach(well => {
      let sampleName = well.sampleName;
      if (set.has(sampleName)) {
        return;
      }
      set.add(sampleName);
      let sum = 0, num = 0, s = 0;
      //  将所有 sampleName 一致的 sq 作累加
      wellList.forEach(item => {
        if (sampleName !== item.sampleName) {
          return;
        }
        let sq = item.lines[channelIndex].sq;
        if (sq === -1) {
          return;
        }
        sum += sq;
        num++;
      });
      if (num === 0) {
        return;
      }
      let sqMean = sum / num;
      // 对所有 sample name 一致的 cqMean 赋值。同时为计算 sqSd 做准备
      wellList.forEach(item => {
        let index = item.row * 12 + item.col;
        if (sampleName !== item.sampleName) {
          return;
        }
        let sq = item.lines[channelIndex].sq;
        if (sq === -1) {
          return;
        }
        data.wells[index].lines[channelIndex].sqMean = sqMean;
        s += Math.pow(sq - sqMean, 2);
      });
      // 对所有 sampleName 一致的 sqSd 赋值。
      let sqSd = Math.sqrt(s / num);
      wellList.forEach(item => {
        let index = item.row * 12 + item.col;
        if (sampleName !== item.sampleName) {
          return;
        }
        let sq = item.lines[channelIndex].sq;
        if (sq === -1) {
          return;
        }
        data.wells[index].lines[channelIndex].sqSd = sqSd;
      });
    });
  });
}

/**
 *
 * 找出数组中第一大,第二大,第三大的值
 *
 * @param arr
 * @returns {{secondMax, firstMax, thirdMax}}
 */
export function findMax(arr) {
  let tempArr = JSON.parse(JSON.stringify(arr));
  tempArr.sort((a, b) => b - a);
  let firstMax = tempArr[0], secondMax = tempArr[1], thirdMax = tempArr[2];
  return {
    firstMax,
    secondMax,
    thirdMax
  };
}


//计算arctan的角度值转化为度数
export function arcTanValue(y, x) {
  if (x === 0 && y !== 0) {
    return [90, 0];
  } else if (x === 0 && y === 0) {
    return [0, 0];
  } else {
    return [Math.atan(y / x) * 180 / Math.PI < 0 ? 0 : Math.atan(y / x) * 180 / Math.PI, 0];
  }
}

//根据斜率转化为角度值
export function slopToAngle(y, x) {
  if (x === 0 && y !== 0) {
    return 90;
  } else if (x === 0 && y === 0) {
    return 0;
  } else {
    return Math.atan(y / x) * 180 / Math.PI;
  }
}

function getCirclePoint(x1, y1, x2, y2, x3, y3) {
  return {
    x: ((y2 - y1) * (y3 - y2) * (x1 - x3) + (y3 - y2) * (x2 + x1) - (y2 + y1) * (x3 + x2)) / (2 * (y3 - y2) * (x1 - x2) - 2 * (y2 - y1) * (x3 - x2)),
    y: ((x2 - x1) * (x3 - x2) * (y1 - y3) + (x3 - x2) * (y2 + y1) - (x2 + x1) * (y3 + y2)) / (2 * (x3 - x2) * (y1 - y2) - 2 * (x2 - x1) * (y3 - y2))
  };
}

export function getCircle(x1, y1, x2, y2) {
  let mid = [(x1 + x2) / 2, (y1 + y2) / 2];
  let k = (y1 - y2) / (x1 - x2);
  let k1 = -1 / k;//垂直平分线斜率
  //与y轴的截距
  let b = mid[1] - k1 * mid[0];
  //与x轴截距
  let x = -b / k1;
  let center, radius;
  //采用负的值
  if (b < 0) {
    //与y轴的截距作为第三个点
    center = getCirclePoint(x1, y1, x2, y2, 0, b);
    radius = Math.sqrt((center.y - b) * (center.y - b) + center.x * center.x);
  } else {
    center = getCirclePoint(x1, y1, x2, y2, x, 0);
    // radius = Math.sqrt(center.y * center.y + (center.x - x) * (center.x - x));
    radius = x2; //测试规整圆
  }
  return {
    center: [center.x, center.y],
    radius: x2,//长半轴x方向
    x1y1: y1,
    x2y2: x2
  };
}

//根据角度值转化为斜率
export function angleToSlop(angle) {
  return Math.tan(angle * Math.PI / 180);
}

//获取target在data数组中离某个值最近的索引
export function getTargetIndex(data, target, resultKM) {
  if (resultKM) {
    let indey = -1;
    for (let i = 0; i < resultKM.clusters.length; i++) {
      let item = resultKM.clusters[i];
      for (let j = 0; j < item.length; j++) {
        if (item[j][0] === target) {
          indey = i;
        }
      }
    }
    if (indey === -1) {
      return -1;
    } else {
      let findIndex = data.findIndex(item => item === resultKM.centroids[indey][0]);
      return findIndex;
    }
  } else {
    return -1;
  }
}

/*
 * K-Means算法，输入参数包含输入的data点数据和分组数目，返回最终的分组的划分值和对应每组的数据
 * */
export function kMeans(data, k = 3) {
  try {
    if (k <= 0 || !Array.isArray(data) || data.length === 0) {
      console.error("Invalid input parameters");
      return;
    }
    let obj = {};
    let count = 0;
    for (let i = 0; i < data.length; i++) {
      if (obj.hasOwnProperty(data[i][0]) || isNaN(data[i][0])) {
      } else {
        count++;
        if (count > 3) {
          break;
        }
        obj[data[i][0]] = data[i];
      }
    }
    let strings = Object.keys(obj).map(it => Number(it));
    let strings_ = Object.values(obj).map(it => Number(it[1]));
    let initArr = [[strings[0], strings_[0]], [strings[1], strings_[1]], [strings[2], strings_[2]]];
    let centroids = [[15, strings_[0]], [45, strings_[1]], [75, strings_[2]]];
    //TODO：够分3组的情况
    if (strings.length >= k) {
      while (true) {
        let clusters = assignPointsToClusters(centroids);
        //centroids有NAN有问题
        let flag1 = clusters.some(it => it.length < 1);
        if (flag1) {
          //说明初始值选的过于不合适，应重新选择初始值
          centroids = initArr;
        } else {
          let newCentroids = updateCentroids(clusters);
          if (hasConverged(centroids, newCentroids)) {
            return {
              centroids: newCentroids,
              clusters: clusters
            };
          }
          centroids = newCentroids;
        }
      }
    } else {
      console.log('有效数据少于3个');
    }

    function assignPointsToClusters(centroids_) {
      let clusters = new Array(k).fill(0).map(() => []);
      for (let point of data) {
        let closestCentroidIndex = 0;
        let minDistance = Infinity;
        for (let i = 0; i < centroids_.length; i++) {
          let distance = euclideanDistance(point, centroids_[i]);
          if (distance < minDistance) {
            closestCentroidIndex = i;
            minDistance = distance;
          }
        }
        clusters[closestCentroidIndex].push(point);
      }
      return clusters;
    }

    function updateCentroids(clusters) {
      return clusters.map(cluster => {
        if (cluster.length > 0) {
          let sum = cluster.reduce((acc, point) => {
            return acc.map((val, i) => val + point[i]);
          }, new Array(cluster[0].length).fill(0));
          return sum.map(val => val / cluster.length);
        }
      });
    }

    function euclideanDistance(a, b) {
      try {
        return Math.sqrt(a.reduce((acc, val, i) => acc + Math.pow(val - b[i], 2), 0));
      } catch (e) {
        console.warn(e);
      }
    }

    function hasConverged(oldCentroids, newCentroids) {
      return oldCentroids.every((centroid, i) => {
        return euclideanDistance(centroid, newCentroids[i]) < 1e-9;
      });
    }
  } catch (e) {
    console.log(e);
    console.log('检查数据');
  }
}
